package org.jeecg.common.util.valid;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.jeecg.common.core.Result;
import org.jeecg.common.rc.aop.dicttrans.dictaop.entity.Describetor;
import org.jeecg.common.util.RefUtil;
import org.jeecg.common.util.spelutil.SpELUtils;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;

import javax.validation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.hutool.core.util.StrUtil.isBlank;

/**
 * valid校验封装
 *
 * @author zjarlin
 * @since 2022/07/19
 */
@Component
public class Valids {
    /**
     * @param list
     * @param consumer 批量导入方法
     * @return {@link Result }<{@link ValidVO }>
     * @author zjarlin
     * @since 2023/10/07
     */
    public static <T> Result<ValidVO> returnValidList(List<T> list, final Consumer<List<T>> consumer) {
        if (CollUtil.isEmpty(list)) {
            return Result.error("导入失败,列表为空");
        }
        Class<T> tClass = (Class<T>) list.get(0).getClass();
        List<JSONObject> validate = validate(list);


        Map<Boolean, List<JSONObject>> all = validate.stream().collect(Collectors.partitioningBy(e -> {
            String msg = (String) e.get("msg");
            return isBlank(msg);
        }));
        List<JSONObject> successList = all.get(true);
        List<JSONObject> errorList = all.get(false);


        if (CollUtil.isNotEmpty(successList)) {
            List<T> collect = successList.stream().map(e -> {
                T t = JSON.parseObject(e.toJSONString(), tClass);
                return t;
            }).collect(Collectors.toList());
            consumer.accept(collect);
        }
        int errorSize = errorList.size();
        int successNo = successList.size();
        ValidVO objectValidList = new ValidVO();
        objectValidList.setErrorNo(errorSize);
        objectValidList.setSuccessNo(successNo);
        objectValidList.setSuccessList(successList);
        objectValidList.setErrorList(errorList);
        objectValidList.setErrorList(errorList);
        if (errorSize > 0) {
            return Result.OK("失败条数" + errorSize + "成功条数" + successNo, objectValidList);
        }
        return Result.OK("全部导入成功!" + "成功条数" + successNo);
    }

    /**
     * 校验list增加一列报错信息
     *
     * @param list 列表 入参
     * @return {@link List }<{@link JSONObject }>
     * @author zjarlin
     * @since 2023/04/15
     */
    public static <T> List<JSONObject> validate(List<T> list) {
        String jsonString = JSON.toJSONString(list);
        System.out.println(jsonString);
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        List<JSONObject> msg1 = list.stream().map(e -> {
            JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(e));
            List<Des> constraintViolations = getValidDes(e);
            String msg = constraintViolations.stream().map(des -> {
                String fieldComment = des.getFieldComment();
                String message = des.getMessage();
                String s1 = StrUtil.addPrefixIfNot(message, fieldComment);
                return s1;
            }).collect(Collectors.joining(
//                    ","
//                    +
                    System.lineSeparator()
            ));
            Map<String, List<Des>> describe = constraintViolations.stream().collect(Collectors.groupingBy(
                    Des::getFieldName
            ));
            jsonObject.put("msg", msg);
            jsonObject.put("describe", describe);
            return jsonObject;
        }).collect(Collectors.toList());
        return msg1;
    }

    public static <T> List<Des> getValidDes(T item) {
//        ValidatorFactory factory = null == validatorFactories ? Validation.buildDefaultValidatorFactory():;
        Configuration<?> configure = Validation.byDefaultProvider().configure();
        ValidatorFactory factory = configure.buildValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<T>> validate = validator.validate(item);
        List<Des> collect = validate.stream().map(e -> {
            Class<T> rootBeanClass = e.getRootBeanClass();
            String fieldName = e.getPropertyPath().toString();
            Field field = ReflectUtil.getField(rootBeanClass, fieldName);
//            boolean customCondition = field.isAnnotationPresent(CustomCondition.class);
            String fieldComment = Optional.ofNullable(field.getAnnotation(ApiModelProperty.class)).map(a -> a.value()).orElse("");
            String message = e.getMessage();
            //处理没注释情况
            message = StrUtil.addPrefixIfNot(message, fieldComment);
            Des des = new Des();
            des.setFieldName(fieldName);
            des.setFieldComment(fieldComment);
            des.setMessage(message);
            return des;
        }).collect(Collectors.toList());
        //处理自定义注解的提示消息
        List<Des> collect2 = getCustomDes(item);
        collect.addAll(collect2);
        return collect;
    }

    /**
     * 处理自定义注解的提示消息
     *
     * @param item 入参
     * @return {@link List }<{@link Des }>
     * @author zjarlin
     * @since 2023/10/07
     */
    @NotNull
    private static <T> List<Des> getCustomDes(T item) {
        Class<?> aClass = item.getClass();
        List<Describetor<CustomValidator>> annoMap= RefUtil.findAnnoMap(item, CustomValidator.class, CustomValidators.class, CustomValidators::value, false,true);
        List<Des> collect2 = annoMap.stream()
                .flatMap(e -> {
                    String fieldName = e.getFieldName();
                    List<CustomValidator> value = e.getNeedSearchAnnotations();
                    Stream<Des> desStream = value.stream()
                            //不进行空值校验的进行
//                    .filter(customanno -> !customanno.checkNull())
                            .map(customanno -> {
                                String expression = customanno.expression();
                                Method methodByName = ReflectUtil.getMethodByName(aClass, expression);
                                //校验是否通过
                                boolean useSpel = customanno.useSpel();
                                if (!useSpel && Objects.isNull(methodByName)) {
                                    throw new RuntimeException("被校验对象没有" + expression + "成员方法");
                                }

//                        methodByName即spel表达式
//        stringObjectHashMap.put("thisObj", ctx);
                                Boolean verificationPassed = !useSpel
                                        ? ReflectUtil.invoke(item, methodByName)
                                        : SpELUtils.evaluateExpression(item, expression, Boolean.class);
                                //默认情况下,为空不进行校验
//                verificationPassed=isCheckNull?verificationPassed:false;
                                Des des = null;
//                                默认对空不校验
                                // 如果校验不通过

                                boolean checkNull = customanno.checkNull();
                                boolean b = !checkNull && !verificationPassed || checkNull && Objects.isNull(ReflectUtil.getFieldValue(item, fieldName));

//                                boolean b1 = (checkNull ^ verificationPassed) && Objects.isNull(ReflectUtil.getFieldValue(item, fieldName));
                                if (b) {
                                    Field field = ReflectUtil.getField(aClass, fieldName);
                                    String fieldComment = Optional.ofNullable(field.getAnnotation(ApiModelProperty.class)).map(x -> x.value()).orElse("");
                                    String message = customanno.message();
                                    message = StrUtil.addPrefixIfNot(message, fieldComment);
                                    des = new Des();
                                    des.setFieldName(fieldName);
                                    des.setFieldComment(fieldComment);
                                    des.setMessage(message);
                                }
                                return des;
                            }).filter(Objects::nonNull);
                    return desStream;
                }).collect(Collectors.toList());
        return collect2;
    }


    @Data
    public static class ValidVO {
        Integer errorNo;
        Integer successNo;
        List<?> successList;
        List<?> errorList;
    }

    @Data
    public static
    class Des {
        String fieldName;
        String fieldComment;
        String message;
    }
//    @Autowired
//    public void setValidator(Validator validator) {
//        Valids.validator = validator;
//    }
}
